#ifndef BTL_IMAGE_IMAGE
#define BTL_IMAGE_IMAGE

// Boost.Config used to determine if rvalue-references are supported
#include <boost/config.hpp>
#include <Eigen/Dense>
#include <btl/Image/PixelTypes.hpp>

namespace btl
{
namespace image
{

template <class PixelT> class ImageRegion;

template <class PixelT>
class ImageRegion<const PixelT>
{
   public:
      typedef PixelT pixel_type;
      typedef pixel_type* pointer;
      typedef pixel_type& reference;
      typedef const pixel_type* const_pointer;
      typedef const pixel_type& const_reference;

      ImageRegion();
      ImageRegion(const ImageRegion& r);
      ImageRegion(const_pointer pData, int w, int h);
      ImageRegion(const_pointer pData, int w, int h, int stride);

      ImageRegion& operator=(const ImageRegion& r);

      bool null() const;
      int width() const;
      int height() const;
      int stride() const;

      const_reference operator()(int i, int j) const;
      const_pointer operator[](int i) const;
      const_pointer data() const;
      ImageRegion region(int x, int y, int w, int h) const;
   protected:
      pointer _pData;
      int _w;
      int _h;
      int _stride;
};

template <class PixelT>
class ImageRegion : public ImageRegion<const PixelT>
{
   public:
      ImageRegion();
      ImageRegion(const ImageRegion& r);
      ImageRegion(typename ImageRegion::pointer pData, int w, int h);
      ImageRegion(typename ImageRegion::pointer pData, int w, int h, int stride);

      typename ImageRegion::reference operator()(int i, int j);
      typename ImageRegion::pointer operator[](int i);
      typename ImageRegion::pointer data();
      ImageRegion region(int x, int y, int w, int h);

      void copy(const ImageRegion<const PixelT>& from);
      void fill(const PixelT& value);
};

template <class PixelT>
class Image : public ImageRegion<PixelT>
{
   public:
      Image();
      Image(int w, int h);
      Image(int w, int h, const PixelT& fill_value);
      ~Image();

      // copy or assign from an equal type of image
      Image(const Image&);
      Image& operator=(const Image&);

      // copy or assign from other images
      Image(const ImageRegion<const PixelT>&);
      Image& operator=(const ImageRegion<const PixelT>&);

#ifndef BOOST_NO_RVALUE_REFERENCES
      // support cheap destructive transfer
      Image(Image && from);
      Image& operator=(Image && from);
#endif

      void clone(const ImageRegion<const PixelT>& r);
      void allocate(int w, int h);
      void allocate(int w, int h, const PixelT& fill_value);
      void reset();
};

// FIXME: perhaps extend this to allow a row alignment specification?
template <class PixelT, int WidthT, int HeightT>
class SmallImage
{
   public:
      typedef PixelT pixel_type;
      typedef pixel_type* pointer;
      typedef pixel_type& reference;
      typedef const pixel_type* const_pointer;
      typedef const pixel_type& const_reference;
      typedef ImageRegion<PixelT> region_type;
      typedef const ImageRegion<const PixelT> const_region_type;

      static const int Width = WidthT;
      static const int Height = HeightT;

      SmallImage();
      explicit SmallImage(const PixelT& fill_value);

      // copy or assign from equal types
      SmallImage(const SmallImage& im);
      SmallImage& operator=(const SmallImage& im);

      // you can implicitly turn an ImageRegion<> into a SmallImage<>
      SmallImage(const_region_type& r);
      SmallImage& operator=(const_region_type& im);
      // or a SmallImage<> into an ImageRegion<>
      operator region_type();
      operator const_region_type() const;

      // provide the same interface as ImageRegion
      bool null() const;
      int width() const;
      int height() const;
      int stride() const;

      const_reference operator()(int i, int j) const;
      const_pointer operator[](int i) const;
      const_pointer data() const;
      const_region_type region(int x, int y, int w, int h) const;

      reference operator()(int i, int j);
      pointer operator[](int i);
      pointer data();
      region_type region(int x, int y, int w, int h);

      void copy(const_region_type& from);
   private:
      PixelT _buf[Width*Height];
};

typedef ImageRegion<const PixelGrey> ImageRegionConstGrey;
typedef ImageRegion<const PixelRGB> ImageRegionConstRGB;
typedef ImageRegion<const PixelFloat> ImageRegionConstFloat;

typedef ImageRegion<PixelGrey> ImageRegionGrey;
typedef ImageRegion<PixelRGB> ImageRegionRGB;
typedef ImageRegion<PixelFloat> ImageRegionFloat;

typedef Image<PixelGrey> ImageGrey;
typedef Image<PixelRGB> ImageRGB;
typedef Image<PixelFloat> ImageFloat;

} // namespace image
} // namespace btl

namespace btl
{

using image::ImageRegion;

using image::ImageRegionConstGrey;
using image::ImageRegionConstRGB;
using image::ImageRegionConstFloat;

using image::ImageRegionGrey;
using image::ImageRegionRGB;
using image::ImageRegionFloat;

using image::Image;

using image::ImageGrey;
using image::ImageRGB;
using image::ImageFloat;

using image::SmallImage;

}

// ====================================================================
// === Implementation

#include <algorithm> // for std::copy, std::fill
#include <cassert>

namespace btl
{
namespace image
{

// ~~~~~~~~~~ ImageRegion<const PixelT> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

template <class PixelT>
inline ImageRegion<const PixelT>::ImageRegion():
   _pData(0), _w(0), _h(0), _stride(0) {}

template <class PixelT>
inline ImageRegion<const PixelT>::ImageRegion(const ImageRegion& r):
   _pData(r._pData), _w(r._w), _h(r._h), _stride(r._stride) {}

// Const on the incoming _pData pointer is cast away with const_cast.
// this should be "safe", because ImageRegion<const T> never modifies
// the data pointed to by its _pData member, and only provides public
// access via const pointers and references.
//
// The reason we don't just make the _pData member const itself is
// that we want ImageRegion<T> to be derived from ImageRegion<const T>,
// and ImageRegion<T> (which has constructors that take *non-const*
// pointers to data only) requires non-const access to the image data.
//
// It's important to have that hierarchy, because it means that
// operations written to work with const ImageRegion<const T>&
// (for some T) will also work seamlessly with ImageRegion<T>
// and Image<T>.

template <class PixelT>
inline ImageRegion<const PixelT>::ImageRegion(const_pointer pData, int w, int h):
   _pData(const_cast<pointer>(pData)), _w(w), _h(h), _stride(sizeof(pixel_type)*w)
{
   assert(w >= 0 && h >= 0);
}

template <class PixelT>
inline ImageRegion<const PixelT>::ImageRegion(const_pointer pData, int w, int h, int stride):
   _pData(const_cast<pointer>(pData)), _w(w), _h(h), _stride(stride)
{
   assert(w >= 0 && h >= 0);
}

template <class PixelT>
inline ImageRegion<const PixelT>& ImageRegion<const PixelT>::operator=(const ImageRegion& r)
{
   this->_pData = r._pData;
   this->_w = r._w;
   this->_h = r._h;
   this->_stride = r._stride;
   return *this;
}

template <class PixelT>
inline bool ImageRegion<const PixelT>::null() const
{
   return (this->_pData == 0);
}

template <class PixelT>
inline int ImageRegion<const PixelT>::width() const
{
   return this->_w;
}

template <class PixelT>
inline int ImageRegion<const PixelT>::height() const
{
   return this->_h;
}

template <class PixelT>
inline int ImageRegion<const PixelT>::stride() const
{
   return this->_stride;
}

template <class PixelT>
inline typename ImageRegion<const PixelT>::const_pointer
ImageRegion<const PixelT>::data() const
{
   return this->_pData;
}

template <class PixelT>
inline ImageRegion<const PixelT>
ImageRegion<const PixelT>::region(int x, int y, int w, int h) const
{
   assert(x >= 0 && y >= 0 && w >= 0 && h >= 0);
   assert(x + w <= this->_w);
   assert(y + h <= this->_h);
   return ImageRegion(&(*this)(y, x), w, h, this->_stride);
}

template <class PixelT>
inline typename ImageRegion<const PixelT>::const_reference
ImageRegion<const PixelT>::operator()(int i, int j) const
{
   assert(j >= 0 && j <= this->_w); // allow one-past-the-end
   assert(i >= 0 && (i < this->_h || (i == this->_h && j == 0)));
   return (*this)[i][j];
}

template <class PixelT>
inline typename ImageRegion<const PixelT>::const_pointer
ImageRegion<const PixelT>::operator[](int i) const
{
   assert(i >= 0 && i <= this->_h); // allow one-past-the-end
   assert(this->_pData != 0);
   return reinterpret_cast<const_pointer>(
             reinterpret_cast<unsigned char*>(this->_pData) + this->_stride * i
          );
}

// ~~~~~~~~~~ ImageRegion<PixelT> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

template <class PixelT>
inline ImageRegion<PixelT>::ImageRegion() {}

template <class PixelT>
inline ImageRegion<PixelT>::ImageRegion(const ImageRegion& r):
   ImageRegion<const PixelT>(r) {}

template <class PixelT>
inline ImageRegion<PixelT>::ImageRegion(typename ImageRegion::pointer pData, int w, int h):
   ImageRegion<const PixelT>(pData, w, h)
{
   assert(w >= 0 && h >= 0);
}

template <class PixelT>
inline ImageRegion<PixelT>::ImageRegion(typename ImageRegion::pointer pData, int w, int h, int stride):
   ImageRegion<const PixelT>(pData, w, h, stride)
{
   assert(w >= 0 && h >= 0);
}

template <class PixelT>
inline typename ImageRegion<PixelT>::reference
ImageRegion<PixelT>::operator()(int i, int j)
{
   assert(j >= 0 && j <= this->_w); // allow one-past-the-end
   assert(i >= 0 && (i < this->_h || (i == this->_h && j == 0)));
   return (*this)[i][j];
}

template <class PixelT>
inline typename ImageRegion<PixelT>::pointer
ImageRegion<PixelT>::operator[](int i)
{
   assert(i >= 0 && i <= this->_h); // allow one-past-the-end
   assert(this->_pData != 0);
   return reinterpret_cast<typename ImageRegion::pointer>(
             reinterpret_cast<unsigned char*>(this->_pData) + this->_stride * i
          );
}

template <class PixelT>
inline typename ImageRegion<PixelT>::pointer
ImageRegion<PixelT>::data()
{
   return this->_pData;
}

template <class PixelT>
inline ImageRegion<PixelT>
ImageRegion<PixelT>::region(int x, int y, int w, int h)
{
   assert(x >= 0 && y >= 0 && w >= 0 && h >= 0);
   assert(x + w <= this->_w);
   assert(y + h <= this->_h);
   return ImageRegion(&(*this)(y, x), w, h, this->_stride);
}

template <class PixelT>
inline void ImageRegion<PixelT>::copy(const ImageRegion<const PixelT>& from)
{
   assert(from.width() == this->_w && from.height() == this->_h);
   if(from.data() != this->_pData)
   {
      // FIXME: check for overlapping memory and abort
      const int h = this->_h;
      const int w = this->_w;
      for(int i = 0; i < h; ++i)
         std::copy(&from(i, 0), &from(i, w), (*this)[i]);
   }
}

template <class PixelT>
inline void ImageRegion<PixelT>::fill(const PixelT& fill_value)
{
   const int h = this->_h;
   const int w = this->_w;
   for(int i = 0; i < h; ++i)
   {
      typename Image<PixelT>::pointer p = (*this)[i];
      std::fill(&p[0], &p[w], fill_value);
   }
}

// ~~~~~~~~~~ Image<PixelT> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

template <class PixelT>
inline Image<PixelT>::Image() {}

template <class PixelT>
inline Image<PixelT>::Image(int w, int h)
{
   assert(w >= 0 && h >= 0);
   allocate(w, h);
}

template <class PixelT>
inline Image<PixelT>::Image(int w, int h, const PixelT& fill_value)
{
   assert(w >= 0 && h >= 0);
   allocate(w, h, fill_value);
}

template <class PixelT>
inline Image<PixelT>::~Image()
{
   delete[] this->_pData;
}

template <class PixelT>
inline Image<PixelT>::Image(const Image& im)
{
   clone(im);
}

template <class PixelT>
inline Image<PixelT>& Image<PixelT>::operator=(const Image& im)
{
   clone(im);
   return *this;
}

template <class PixelT>
inline Image<PixelT>::Image(const ImageRegion<const PixelT>& r)
{
   clone(r);
}

template <class PixelT>
inline Image<PixelT>& Image<PixelT>::operator=(const ImageRegion<const PixelT>& r)
{
   clone(r);
   return *this;
}

#ifndef BOOST_NO_RVALUE_REFERENCES
template <class PixelT>
inline Image<PixelT>::Image(Image && from):
   ImageRegion<PixelT>(from._pData, from._w, from._h, from._stride)
{
   from._pData = 0;
   from._w = from._h = 0;
   from._stride = 0;
}

template <class PixelT>
inline Image<PixelT>& Image<PixelT>::operator=(Image && from)
{
   assert(&from != this); // shouldn't move from an Image to itself

   if(&from != this)
   {
      // ensure we start off blank
      // not strictly required, but without it from could end up
      // owning our old _pData, which would be safe but unexpected
      // (and probably not what the user wants)
      reset();
      std::swap(this->_pData, from._pData);
      std::swap(this->_w, from._w);
      std::swap(this->_h, from._h);
      std::swap(this->_stride, from._stride);
   }
   return *this;
}
#endif

template <class PixelT>
inline void Image<PixelT>::clone(const ImageRegion<const PixelT>& r)
{
   const int w = r.width(), h = r.height();
   allocate(w, h);
   this->copy(r);
}

template <class PixelT>
inline void Image<PixelT>::allocate(int w, int h)
{
   assert(w >= 0 && h >= 0);
   // FIXME: this is almost certainly not exception safe
   // (what happens if PixelT constructor throws?)
   reset();
   this->_pData = new PixelT[w * h];
   this->_w = w;
   this->_h = h;
   this->_stride = sizeof(PixelT) * w;
}

template <class PixelT>
inline void Image<PixelT>::allocate(int w, int h, const PixelT& fill_value)
{
   assert(w >= 0 && h >= 0);
   allocate(w, h);
   fill(fill_value);
}

template <class PixelT>
inline void Image<PixelT>::reset()
{
   // FIXME: check exception safety (can delete[] throw?)
   delete[] this->_pData;
   this->_pData = 0;
   this->_w = this->_h = 0;
   this->_stride = 0;
}

// ~~~~~~~~~~ SmallImage<PixelT, Width, Height> ~~~~~~~~~~~~~~~~~~~~

template <class PixelT, int Width, int Height>
inline SmallImage<PixelT, Width, Height>::SmallImage() {}

template <class PixelT, int Width, int Height>
inline SmallImage<PixelT, Width, Height>::SmallImage(const ImageRegion<const PixelT>& r)
{
   this->copy(r);
}

template <class PixelT, int Width, int Height>
inline SmallImage<PixelT, Width, Height>::SmallImage(const PixelT& fill_value)
{
   std::fill(&this->_buf[0], &this->_buf[Width * Height], fill_value);
}

// copyable and assignable to equal types
template <class PixelT, int Width, int Height>
inline SmallImage<PixelT, Width, Height>::SmallImage(const SmallImage& im)
{
   std::copy(&im._buf[0], &im._buf[Width * Height], this->_buf);
}

template <class PixelT, int Width, int Height>
inline SmallImage<PixelT, Width, Height>&
SmallImage<PixelT, Width, Height>::operator=(const SmallImage& im)
{
   std::copy(&im._buf[0], &im._buf[Width * Height], this->_buf);
   return *this;
}

template <class PixelT, int Width, int Height>
inline SmallImage<PixelT, Width, Height>&
SmallImage<PixelT, Width, Height>::operator=(const ImageRegion<const PixelT>& im)
{
   assert(im.width() == Width && im.height() == Height);
   this->copy(im);
   return *this;
}

template <class PixelT, int Width, int Height>
inline SmallImage<PixelT, Width, Height>::operator region_type()
{
   return region_type(_buf, Width, Height);
}

template <class PixelT, int Width, int Height>
inline SmallImage<PixelT, Width, Height>::operator const_region_type() const
{
   return const_region_type(_buf, Width, Height);
}

template <class PixelT, int Width, int Height>
inline bool SmallImage<PixelT, Width, Height>::null() const
{
   return false;
}

template <class PixelT, int Width, int Height>
inline int SmallImage<PixelT, Width, Height>::width() const
{
   return Width;
}

template <class PixelT, int Width, int Height>
inline int SmallImage<PixelT, Width, Height>::height() const
{
   return Height;
}

template <class PixelT, int Width, int Height>
inline int SmallImage<PixelT, Width, Height>::stride() const
{
   return Width * sizeof(pixel_type);
}

template <class PixelT, int Width, int Height>
inline typename SmallImage<PixelT, Width, Height>::const_reference
SmallImage<PixelT, Width, Height>::operator()(int i, int j) const
{
   assert(j >= 0 && j <= Width); // allow one-past-the-end
   assert(i >= 0 && (i < Height || (i == Height && j == 0)));
   return this->_buf[i * Width + j];
}

template <class PixelT, int Width, int Height>
inline typename SmallImage<PixelT, Width, Height>::const_pointer
SmallImage<PixelT, Width, Height>::operator[](int i) const
{
   assert(i >= 0 && i <= Height); // allow one-past-the-end
   return &this->_buf[i * Width];
}

template <class PixelT, int Width, int Height>
inline typename SmallImage<PixelT, Width, Height>::const_pointer
SmallImage<PixelT, Width, Height>::data() const
{
   return this->_buf;
}

template <class PixelT, int Width, int Height>
inline typename SmallImage<PixelT, Width, Height>::const_region_type
SmallImage<PixelT, Width, Height>::region(int x, int y, int w, int h) const
{
   assert(x >= 0 && y >= 0 && w >= 0 && h >= 0);
   assert(x + w <= Width);
   assert(y + h <= Width);
   return const_region_type(&((*this)(y, x)), w, h, stride());
}


template <class PixelT, int Width, int Height>
inline typename SmallImage<PixelT, Width, Height>::reference
SmallImage<PixelT, Width, Height>::operator()(int i, int j)
{
   assert(j >= 0 && j <= Width); // allow one-past-the-end
   assert(i >= 0 && (i < Height || (i == Height && j == 0)));
   return this->_buf[i * Width + j];
}

template <class PixelT, int Width, int Height>
inline typename SmallImage<PixelT, Width, Height>::pointer
SmallImage<PixelT, Width, Height>::operator[](int i)
{
   assert(i >= 0 && i <= Height); // allow one-past-the-end
   return &this->_buf[i * Width];
}

template <class PixelT, int Width, int Height>
inline typename SmallImage<PixelT, Width, Height>::pointer
SmallImage<PixelT, Width, Height>::data()
{
   return this->_buf;
}

template <class PixelT, int Width, int Height>
inline typename SmallImage<PixelT, Width, Height>::region_type
SmallImage<PixelT, Width, Height>::region(int x, int y, int w, int h)
{
   assert(x >= 0 && y >= 0 && w >= 0 && h >= 0);
   assert(x + w <= Width);
   assert(y + h <= Width);
   return region_type(&((*this)(y, x)), w, h, stride());
}

template <class PixelT, int Width, int Height>
inline void SmallImage<PixelT, Width, Height>::copy(const_region_type& im)
{
   assert(im.width() == Width && im.height() == Height);
   if(im.data() != this->_buf)
   {
      // FIXME: check for overlapping memory and abort
      for(int i = 0; i < Height; ++i)
         std::copy(&im(i, 0), &im(i, Width), (*this)[i]);
   }
}

} // namespace image
} // namespace btl

#endif
